#include "StdAfx.h"
#include "DX11renderer.h"

namespace banknamespace
{
	DX11renderer::DX11renderer(void)
	{
		device = NULL;
		context = NULL;
		swapchain = NULL;
		rtview = NULL;
		dsv = NULL;
		rs = NULL;
		/*texture = NULL;
		indexBuffer = NULL;
		vertexBuffer = NULL;

		effect = NULL;
		g_InputLayout = NULL;*/
	}


	DX11renderer::~DX11renderer(void)
	{
		// clean up
		device->Release();
		context->Release();
		swapchain->Release();
		rtview->Release();
		dsv->Release();
		rs->Release();
		/*texture->Release();
		indexBuffer->Release();
		vertexBuffer->Release();

		effect->Release();
		g_InputLayout->Release();*/
	}

	void DX11renderer::init( HWND& m_hWnd, HDC& m_hdcWindow )
	{
		//this->m_hWnd = &m_hWnd;

		RECT rc;
		GetClientRect( m_hWnd, &rc );
		int width = rc.right;
		int height = rc.bottom;

		DXGI_SWAP_CHAIN_DESC sd =
		{
			{ width, height, { 60, 1 }, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, DXGI_MODE_SCALING_UNSPECIFIED },
			{ 1, 0 },
			DXGI_USAGE_RENDER_TARGET_OUTPUT,
			1,
			m_hWnd,
			true,//!fullscreen,
			DXGI_SWAP_EFFECT_DISCARD,
			0
		};

		D3D_FEATURE_LEVEL featurelevels[] = 
		{
			D3D_FEATURE_LEVEL_11_0,
			D3D_FEATURE_LEVEL_10_1,
			D3D_FEATURE_LEVEL_10_0,
		};

		D3D_FEATURE_LEVEL featurelevelpicked;

		UINT createdeviceflags = 0;
		#ifdef _DEBUG
			createdeviceflags |= D3D11_CREATE_DEVICE_DEBUG;
		#endif

		HV(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createdeviceflags, featurelevels, sizeof(featurelevels)/sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &sd, &swapchain, &device, &featurelevelpicked, &context));
	
		ID3D11Texture2D *backbuffer;
		HV(swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **)&backbuffer));
		HV(device->CreateRenderTargetView(backbuffer, NULL, &rtview));
		HV(backbuffer->Release());
	
		context->OMSetRenderTargets(1, &rtview, dsv);

		D3D11_VIEWPORT vp;
		vp.Width = (FLOAT)(rc.right - rc.left);
		vp.Height = (FLOAT)(rc.bottom - rc.top);
		vp.MinDepth = 0.0f;
		vp.MaxDepth = 1.0f;
		vp.TopLeftX = 0;
		vp.TopLeftY = 0;
		context->RSSetViewports( 1, &vp );

		D3D11_RASTERIZER_DESC rasterizerstate = { D3D11_FILL_SOLID, D3D11_CULL_NONE, true, 0, 0, 0, true, false, false, false };
		HV(device->CreateRasterizerState(&rasterizerstate, &rs));
		context->RSSetState(rs); 

		D3D11_TEXTURE2D_DESC desc =
		{
			width, height,
			1, 1,
			DXGI_FORMAT_D32_FLOAT,
			{ 1, 0 },
			D3D11_USAGE_DEFAULT,
			D3D11_BIND_DEPTH_STENCIL,
			0, 0
		};
		ID3D11Texture2D *tex;
		HV(device->CreateTexture2D(&desc, NULL, &tex));
		HV(device->CreateDepthStencilView(tex, NULL, &dsv));
		tex->Release();
		
		context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	}

	// DX method
	void DX11renderer::clear()
	{
		float ClearColor[4] = {0.2f, 0.2f, 0.24f, 1.0f};
		context->ClearRenderTargetView( rtview, ClearColor );
		context->ClearDepthStencilView( dsv, D3D11_CLEAR_DEPTH, 1.0f, 0 );
	}
	void DX11renderer::swapBuffer()
	{
		swapchain->Present( 0, 0 );
	}

	unsigned int DX11renderer::loadTexture( const char* szFilePath )
	{
		ID3D11ShaderResourceView* tmp;
		//Create Texture
		D3DX11_IMAGE_LOAD_INFO loadInfo;

		wchar_t* w_szTexturePath = CharToWChar(szFilePath);
		HRESULT res = D3DX11CreateShaderResourceViewFromFile(device, w_szTexturePath, &loadInfo, NULL, &tmp, NULL );
		delete w_szTexturePath;
		
		if (res != S_OK)
		{
			return 0;
		}
		else
		{
			textureResourceArr.push_back(tmp);
			return textureResourceArr.size() - 1;
		}
	}
	unsigned int DX11renderer::loadMemoryTexture( unsigned char* pixels, int width, int height )
	{
		ID3D11ShaderResourceView* tmp;

		D3D11_TEXTURE2D_DESC desc = 
		{
			width, height,
			1,1,
			DXGI_FORMAT_R32G32B32_UINT,
			{1,0},
			D3D11_USAGE_DYNAMIC,
			D3D11_BIND_SHADER_RESOURCE,
			D3D11_CPU_ACCESS_WRITE,
			0
		};

		D3D11_SUBRESOURCE_DATA initdata;
		initdata.pSysMem = pixels;
		initdata.SysMemPitch = width * sizeof(int);
		initdata.SysMemSlicePitch = 0;

		ID3D11Texture2D *tex = NULL;
		device->CreateTexture2D( &desc, &initdata, &tex );

		HRESULT res = device->CreateShaderResourceView( tex, NULL, &tmp );
		tex->Release();

		if (res != S_OK)
		{
			return 0;
		}
		else
		{
			textureResourceArr.push_back(tmp);
			return textureResourceArr.size() - 1;
		}
	}
	void DX11renderer::bindTexture( unsigned int textureState, unsigned int textureId )
	{

	}
	void DX11renderer::disableTexture( unsigned int textureState )
	{

	}

	int DX11renderer::createProgram(const char* programName, std::vector<attrStruct>& attrString, std::vector<const char*>& uniformString)
	{
		///// compile .fx file
		ID3D10Blob *fxblob = NULL, *errblob = NULL;
	
		wchar_t* w_programName = CharToWChar(programName);
		std::wstring name = w_programName;
		delete w_programName;

		std::wstring filepath = L"Shader_HLSL/" + name + L".fx";
		HRESULT res = D3DX11CompileFromFile(filepath.c_str(), NULL, NULL, "", "fx_5_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, NULL, &fxblob, &errblob, NULL);
		if (res != S_OK)
		{
			if (errblob)
			{
				char* tmp = (char *)errblob->GetBufferPointer();

				size_t requiredSize = 0;
				::mbstowcs_s(&requiredSize,NULL,0,tmp,0);
				wchar_t* printText = new wchar_t[requiredSize + 1];
				::mbstowcs_s(&requiredSize, printText,requiredSize + 1, tmp,requiredSize);
		
				MessageBox(NULL, printText, L"Fail", 0);
				errblob->Release();
			}
			else
			{
				MessageBox(NULL, L"fx file not load", L"Fail", 0);
			}
		}
	
		ID3DX11Effect* tmpEffect;
		HV(D3DX11CreateEffectFromMemory(fxblob->GetBufferPointer(), fxblob->GetBufferSize(), 0, device, &tmpEffect));
		fxblob->Release();

		unsigned int len = attrString.size();
		D3D11_INPUT_ELEMENT_DESC *decl = new D3D11_INPUT_ELEMENT_DESC[len];
		memset(decl, 0, sizeof(D3D11_INPUT_ELEMENT_DESC)*len);

		int semidx = 0;
		for (unsigned int i = 0; i < len; ++i)
		{
			D3D11_INPUT_ELEMENT_DESC &d = decl[i];
			d.AlignedByteOffset = attrString[i].offset;
			d.SemanticName = attrString[i].name; 
			switch (attrString[i].attrType)
			{
			case S_FLOAT4 : 
				{
					//d.Format = DXGI_FORMAT_R32G32B32_FLOAT;
					break;
				}
			case S_FLOAT3 : 
				{
					d.Format = DXGI_FORMAT_R32G32B32_FLOAT;
					break;
				}
			case S_FLOAT2 : 
				{
					d.SemanticName = "TEXCOORD"; 
					d.Format = DXGI_FORMAT_R32G32_FLOAT;
					d.SemanticIndex = semidx++;
					break;
				}
			default: assert(0);
			}
		}
		
		D3DX11_PASS_DESC effectVsDesc;
		HV(tmpEffect->GetTechniqueByIndex(0)->GetPassByIndex(0)->GetDesc(&effectVsDesc));

		ID3D11InputLayout* tmpInputLayout;
		HV(device->CreateInputLayout(
		  decl, len, effectVsDesc.pIAInputSignature, effectVsDesc.IAInputSignatureSize, &tmpInputLayout));
		delete[] decl;

		effectArr.push_back( tmpEffect );
		inputLayoutArr.push_back( tmpInputLayout );

		return effectArr.size() - 1;
	}
	void DX11renderer::useProgram(int progID)
	{
		effectArr[progID]->GetTechniqueByIndex(0)->GetPassByIndex(0)->Apply(0, context);
		context->IASetInputLayout( inputLayoutArr[progID] );

		currentProgram = progID;
	}
	int DX11renderer::getLocation(const char* name)
	{
		return 0;
	}
	void DX11renderer::setVariable(int ulocation, shaderType type, void* value)
	{
		switch (type)
		{
		case S_MAT4 : 
			{
				effectArr[currentProgram]->GetVariableByName("wvp")->AsMatrix()->SetMatrix( reinterpret_cast<float*>( value ) );
				break;
			}
		case S_VEC4 : 
			{
				//glUniform4fv( uniformLocation[currentProgram][name], 4, reinterpret_cast<float*>( value ) ); 
				break;
			}
		case S_FLOAT : 
			{
				//glUniform1f( uniformLocation[currentProgram][name], *(reinterpret_cast<float*>( value )) );
				break;
			}
		case S_TEXTURE : 
			{
				effectArr[currentProgram]->GetVariableByName("diftex")->AsShaderResource()->SetResource( textureResourceArr[2] );
				break;
			}
		default: break;
		}
	}

	unsigned int DX11renderer::createBuffer( void* start, int bufferSize, bufferType type )
	{
		ID3D11Buffer* tmpBuffer;
		D3D11_SUBRESOURCE_DATA srd = { start, 0, 0 };

		HV(device->CreateBuffer(&CD3D11_BUFFER_DESC(bufferSize, (type == B_INDEXBUFFER) ? D3D11_BIND_INDEX_BUFFER : D3D11_BIND_VERTEX_BUFFER, D3D11_USAGE_IMMUTABLE), &srd, &tmpBuffer));
		bufferArr.push_back( tmpBuffer );
		
		return bufferArr.size() - 1;
	}
	void DX11renderer::drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex)
	{
		UINT stride = strideSize;
		UINT baseVert = baseVertex;
		UINT vertexOffset = baseVert * sizeof(unsigned int);

		context->IASetVertexBuffers( 0, 1, &bufferArr[idVBO], &stride, &vertexOffset );
		context->IASetIndexBuffer( bufferArr[idIBO], DXGI_FORMAT_R32_UINT, startIndex * sizeof(unsigned int) );

		context->DrawIndexed( indexCount, 0, 0 );
	}
}
